/*
 * Copyright(C) 2013 Agree Corporation. All rights reserved.
 *
 * Contributors:
 *     Agree Corporation - initial API and implementation
 */
package com.jessechanrui.template.core.util;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import java.security.spec.KeySpec;


/**
 * @author chenrui
 * @version 1.0
 * @description DES3
 * @date 2025/8/8
 */
public class DesUtils {

    private static final byte NULL_BYTE = 0x00;
    private static final String KFT_DES = "Des";
    private static final String KFT_DESEDE = "Desede";
    private static final String DES_NO_PADDING_MODE = "Des/ECB/NoPadding";
    private static final String DESEDE_NO_PADDING_MODE = "Desede/ECB/NoPadding";

    /**
     * DES加密
     *
     * @param data 明文数据
     * @param key 密钥
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
        return doDesForV1(padNullBytes(data), key, Cipher.ENCRYPT_MODE);
    }

    /**
     * DES加密
     *
     * @param data           明文数据
     * @param key            密钥
     * @param transformation 操作模式
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, byte[] key, String transformation)
            throws Exception {
        KeySpec keySpec = new DESKeySpec(key);
        return doDes(data, transformation, KFT_DES, keySpec, Cipher.ENCRYPT_MODE);
    }

    /**
     * DES解密
     *
     * @param data 密文数据
     * @param key 密钥
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        byte[] plaintext = doDesForV1(data, key, Cipher.DECRYPT_MODE);
        return removeNullBytes(plaintext);
    }

    /**
     * DES
     *
     * @param data           密文数据
     * @param key            密钥
     * @param transformation 操作模式
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, byte[] key, String transformation)
            throws Exception {
        KeySpec keySpec = new DESKeySpec(key);
        return doDes(data, transformation, KFT_DES, keySpec, Cipher.DECRYPT_MODE);
    }

    /**
     * 3DES加密
     *
     * @param data 明文数据
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public static byte[] encrypt3(byte[] data, byte[] key) throws Exception {
        return doDesForV3(padNullBytes(data), key, Cipher.ENCRYPT_MODE);
    }

    /**
     * 3DES加密
     *
     * @param data           明文数据
     * @param key            密钥
     * @param transformation 操作模式
     * @return
     * @throws Exception
     */
    public static byte[] encrypt3(byte[] data, byte[] key, String transformation)
            throws Exception {
        KeySpec keySpec = new DESedeKeySpec(key);
        return doDes(data, transformation, KFT_DESEDE, keySpec, Cipher.ENCRYPT_MODE);
    }

    /**
     * 3DES解密
     *
     * @param 密文数据
     * @param 密钥
     * @return
     * @throws Exception
     */
    public static byte[] decrypt3(byte[] data, byte[] key) throws Exception {
        byte[] plaintext = doDesForV3(data, key, Cipher.DECRYPT_MODE);
        return removeNullBytes(plaintext);
    }

    /**
     * 3DES解密
     *
     * @param data           密文数据
     * @param key            密钥
     * @param transformation 操作模式
     * @return
     * @throws Exception
     */
    public static byte[] decrypt3(byte[] data, byte[] key, String transformation)
            throws Exception {
        KeySpec keySpec = new DESedeKeySpec(key);
        return doDes(data, transformation, KFT_DESEDE, keySpec, Cipher.DECRYPT_MODE);
    }

    private static byte[] doDesForV1(byte[] data, byte[] key, int opmode)
            throws Exception {
        KeySpec keySpec = new DESKeySpec(key);
        return doDes(data, DES_NO_PADDING_MODE, KFT_DES, keySpec, opmode);
    }

    private static byte[] doDesForV3(byte[] data, byte[] key, int opmode)
            throws Exception {
        KeySpec keySpec = new DESedeKeySpec(key);
        return doDes(data, DESEDE_NO_PADDING_MODE, KFT_DESEDE, keySpec, opmode);
    }

    private static byte[] doDes(byte[] data, String transformation, String keyFactoryType,
                                KeySpec keySpec, int opmode) throws Exception {
        Cipher c = Cipher.getInstance(transformation);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(keyFactoryType);
        SecretKey desKey = keyFactory.generateSecret(keySpec);
        c.init(opmode, desKey);
        return c.doFinal(data);
    }

    /**
     * 补充空字节，成8的倍数
     *
     * @param data
     * @return
     */
    private static byte[] padNullBytes(byte[] data) {
        int mod = data.length % 8;
        if (mod == 0) {
            return data;
        }
        byte[] result = new byte[data.length + (8 - mod)];
        System.arraycopy(data, 0, result, 0, data.length);
        for (int i = data.length; i < result.length; i++) {
            result[i] = NULL_BYTE;
        }
        return result;
    }

    /**
     * 去掉多余的空字节
     *
     * @param data
     * @return
     */
    private static byte[] removeNullBytes(byte[] data) {
        int nbCount = 0;
        for (int i = data.length - 1; i >= 0; i--) {
            if (data[i] != NULL_BYTE) {
                break;
            }
            nbCount++;
        }
        if (nbCount > 0) {
            byte[] result = new byte[data.length - nbCount];
            System.arraycopy(data, 0, result, 0, result.length);
            return result;
        } else {
            return data;
        }
    }

}
